{% if HasDescription -%}
/** {{ Description }} */
{% endif -%}
{% if ExportTypes %}export {% endif %}{% if IsAbstract %}abstract {% endif %}class {{ ClassName }}{{ Inheritance }} {
{% for property in Properties -%}
{%   if property.HasDescription -%}
    /** {{ property.Description }} */
{%   endif -%}
    {% if property.IsReadOnly %}readonly {% endif %}{{ property.PropertyName }}{% if property.IsOptional %}?{% elsif RequiresStrictPropertyInitialization %}!{% endif %}: {{ property.Type }}{{ property.TypePostfix }};
{% endfor -%}
{% if HasIndexerProperty -%}

    [key: string]: {{ IndexerPropertyValueType }};
{% endif -%}
{% if HasDiscriminator -%}

    protected _discriminator: string;
{% endif -%}

{% assign condition_temp = HasInheritance == false or ConvertConstructorInterfaceData -%}
{% if GenerateConstructorInterface or HasBaseDiscriminator -%}
    constructor({% if GenerateConstructorInterface %}data?: I{{ ClassName }}{% endif %}) {
{%     if HasInheritance -%}
        super({% if GenerateConstructorInterface %}data{% endif %});
{%     endif -%}
{%     if GenerateConstructorInterface and condition_temp -%}
        if (data) {
{%         if HasInheritance == false -%}
            for (var property in data) {
                if (data.hasOwnProperty(property))
                    (<any>this)[property] = (<any>data)[property];
            }
{%         endif -%}
{%         if ConvertConstructorInterfaceData -%}
{%             for property in Properties -%}
{%                 if property.SupportsConstructorConversion -%}
{%                     if property.IsArray -%}
            if (data.{{ property.PropertyName }}) {
                this.{{ property.PropertyName }} = [];
                for (let i = 0; i < data.{{ property.PropertyName }}.length; i++) {
                    let item = data.{{ property.PropertyName }}[i];
                    this.{{ property.PropertyName }}[i] = item && !(<any>item).toJSON ? new {{ property.ArrayItemType }}(item) : <{{ property.ArrayItemType }}>item;
                }
            }
{%                     elsif property.IsDictionary -%}
            if (data.{{ property.PropertyName }}) {
                this.{{ property.PropertyName }} = {};
                for (let key in data.{{ property.PropertyName }}) {
                    if (data.{{ property.PropertyName }}.hasOwnProperty(key)) {
                        let item = data.{{ property.PropertyName }}[key];
                        this.{{ property.PropertyName }}[key] = item && !(<any>item).toJSON ? new {{ property.DictionaryItemType }}(item) : <{{ property.DictionaryItemType }}>item;
                    }
                }
            }
{%                     else -%}
            this.{{ property.PropertyName }} = data.{{ property.PropertyName }} && !(<any>data.{{ property.PropertyName }}).toJSON ? new {{ property.Type }}(data.{{ property.PropertyName }}) : <{{ property.Type }}>this.{{ property.PropertyName }};
{%                     endif -%}
{%                 endif -%}
{%             endfor -%}
{%         endif -%}
        }
{%     endif -%}
{%     if HasDefaultValues -%}
        {% if GenerateConstructorInterface %}if (!data) {% endif %}{
{%         for property in Properties -%}
{%             if property.HasDefaultValue -%}
            this.{{ property.PropertyName }} = {{ property.DefaultValue }};
{%             endif -%}
{%         endfor -%}
        }
{%     endif -%}
{%     if HasBaseDiscriminator -%}
        this._discriminator = "{{ DiscriminatorName }}";
{%     endif -%}
    }
{% endif -%}

    {% if HasInheritance and SupportsOverrideKeyword %}override {% endif %}init(_data?: any{% if HandleReferences %}, _mappings?: any{% endif %}) {
{% if HasInheritance -%}
        super.init(_data);
{% endif -%}
{% if HasIndexerProperty or HasProperties -%}
        if (_data) {
{%     if HasIndexerProperty -%}
            for (var property in _data) {
                if (_data.hasOwnProperty(property))
                    this[property] = _data[property];
            }
{%     endif -%}
{%     for property in Properties -%}
            {{ property.ConvertToClassCode | strip | tab }}
{%     endfor -%}
        }
{% endif -%}
    }

    static {% if HasInheritance and SupportsOverrideKeyword %}override {% endif %}fromJS(data: any{% if HandleReferences %}, _mappings?: any{% endif %}): {{ ClassName }}{% if HandleReferences %} | null{% endif %} {
        data = typeof data === 'object' ? data : {};
{% if HandleReferences -%}
{%   if HasBaseDiscriminator -%}
{%     for derivedClass in DerivedClasses -%}
        if (data["{{ BaseDiscriminator }}"] === "{{ derivedClass.Discriminator }}")
{%       if derivedClass.IsAbstract -%}
            throw new Error("The abstract class '{{ derivedClass.ClassName }}' cannot be instantiated.");
{%       else -%}
            return createInstance<{{ derivedClass.ClassName }}>(data, _mappings, {{ derivedClass.ClassName }});
{%       endif -%}
{%     endfor -%}
{%   endif -%}
{%   if IsAbstract -%}
        throw new Error("The abstract class '{{ ClassName }}' cannot be instantiated.");
{%   else -%}
        return createInstance<{{ ClassName }}>(data, _mappings, {{ ClassName }});
{%   endif -%}
{% else -%}
{%   if HasBaseDiscriminator -%}
{%     for derivedClass in DerivedClasses -%}
        if (data["{{ BaseDiscriminator }}"] === "{{ derivedClass.Discriminator }}") {
{%       if derivedClass.IsAbstract -%}
            throw new Error("The abstract class '{{ derivedClass.ClassName }}' cannot be instantiated.");
{%       else -%}
            let result = new {{ derivedClass.ClassName }}();
            result.init(data);
            return result;
{%       endif -%}
        }
{%     endfor -%}
{%   endif -%}
{%     if IsAbstract -%}
        throw new Error("The abstract class '{{ ClassName }}' cannot be instantiated.");
{%     else -%}
        let result = new {{ ClassName }}();
        result.init(data);
        return result;
{%     endif -%}
{% endif -%}
    }

    {% if HasInheritance and SupportsOverrideKeyword %}override {% endif %}toJSON(data?: any) {
        data = typeof data === 'object' ? data : {};
{% if HasIndexerProperty -%}
        for (var property in this) {
            if (this.hasOwnProperty(property))
                data[property] = this[property];
        }
{% endif -%}
{% if HasDiscriminator -%}
        data["{{ BaseDiscriminator }}"] = this._discriminator;
{% endif -%}
{% for property in Properties -%}
        {{ property.ConvertToJavaScriptCode | replace: "toISOString","toLocaleString" | tab }}
{% endfor -%}
{% if HasInheritance -%}
        super.toJSON(data);
{% endif -%}
        return data;
    }
{% if GenerateCloneMethod -%}

    clone(): {{ ClassName }} {
{%   if IsAbstract -%}
        throw new Error("The abstract class '{{ ClassName }}' cannot be instantiated.");
{%   else -%}
        const json = this.toJSON();
        let result = new {{ ClassName }}();
        result.init(json);
        return result;
{%   endif -%}
    }
{% endif -%}
}
{% if GenerateConstructorInterface -%}

{%   if HasDescription -%}
/** {{ Description }} */
{%   endif -%}
{% if ExportTypes %}export {% endif %}interface I{{ ClassName }}{{ InterfaceInheritance }} {
{%   for property in Properties -%}
{%       if property.HasDescription -%}
    /** {{ property.Description }} */
{%       endif -%}
    {{ property.PropertyName }}{% if property.IsOptional %}?{% endif %}: {{ property.ConstructorInterfaceType }}{{ property.TypePostfix }};
{%   endfor -%}
{%   if HasIndexerProperty -%}

    [key: string]: {{ IndexerPropertyValueType }};
{%   endif -%}
}
{% endif -%}